
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "testGZCompress.h"
#include "../utils/StringUtils.h"

// add a test for pipe exhaustion - leave stuff in the buffer and call pipeFinal
// add signature verification/checking with no key and see what happens

void tests::testGZCompress(apr_pool_t *mp)
{
   int iters = 10;
   testGZBlockCompress(iters, 10000);
   testGZPipeCompress(iters, 500, 10000, mp);
}

void tests::testGZBlockCompress(int iterations, int max_length)
{
   for (int i = 0; i < iterations; i++)
   {
      // Random length much more than a single key
      int randlen = rand() % max_length;
      // Create a block of (pseudo-)random data
      char *pseudorandom = (char *)malloc(sizeof(char)*randlen);
      for (int j = 0; j < randlen; j++)
         pseudorandom[j] = (rand() & 0xFF0) >> 8;
      // Sign the random data
      apr_size_t new_coded_size = randlen;
      apr_size_t new_plain_size = 0;
      GZCompress *gz_cmp = new GZCompress();
      char *compressed_data = gz_cmp->processDataBlock(pseudorandom, randlen, &new_coded_size);
      delete gz_cmp;
      GZDecompress *gz_dcmp = new GZDecompress();
      char *new_plain_data = gz_dcmp->processDataBlock(compressed_data, new_coded_size, &new_plain_size);
      delete gz_dcmp;
      if (new_plain_size != randlen)
      {
         errprint("error: original data size was %" APR_SIZE_T_FMT ", new data size is %" APR_SIZE_T_FMT, (apr_size_t)randlen, new_plain_size);
      } else if (memcmp(pseudorandom, new_plain_data, sizeof(char)*randlen) != 0)
      {
         errprint("error: original data does not match new data");
         //printf("old data:\n");
         //StringUtils::printBinary(pseudorandom, randlen);
         //printf("new data:\n");
         //StringUtils::printBinary(new_plain_data, randlen);
      } else
      {
         printf("GZ BlockCompression(issame) test %d passed - data length %d (cmp %d)\n", i, randlen, (int)new_coded_size);
      }
      free(pseudorandom);
      free(compressed_data);
      free(new_plain_data);
   }
}

void tests::testGZPipeCompress(int iterations, int max_block, int max_length, apr_pool_t *mp)
{
   for (int i = 0; i < iterations; i++)
   {
      // Random length much more than a single key
      int randlen = rand() % max_length;
      // Create a block of (pseudo-)random data
      char *pseudorandom = (char *)malloc(sizeof(char)*randlen);
      for (int j = 0; j < randlen; j++)
         pseudorandom[j] = (rand() & 0xFF0) >> 8;
      // Encrypt the random data
      apr_size_t new_coded_size = randlen;
      APRBucketBrigade p_input(mp);
      APRBucketBrigade coded(mp);
      GZCompress *gz_cmp = new GZCompress(p_input, coded);
      gz_cmp->processPipeBegin();
      int nidx = 0;
      while (nidx < randlen)
      {
         int randblocklen = rand() % max_block;
         randblocklen = ((randblocklen + nidx) > randlen) ? (randlen - nidx) : randblocklen;
         int randblocklen_added = p_input.writeToStream(&(pseudorandom[nidx]), randblocklen);
         nidx += randblocklen_added;
         gz_cmp->processPipeStep();
      }
      gz_cmp->processPipeEnd();
      delete gz_cmp;
      APRBucketBrigade coded2(mp);
      APRBucketBrigade p_output(mp);
      GZDecompress *gz_dcmp = new GZDecompress(coded2, p_output);
      gz_dcmp->processPipeBegin();
      nidx = 0;
      int total_length = coded.realLength();
      while (nidx < total_length)
      {
         int randblocklen = rand() % max_block;
         randblocklen = ((randblocklen + nidx) > total_length) ? (total_length - nidx) : randblocklen;
         char *working_buffer = (char *)malloc(sizeof(char)*randblocklen);
         int block_read = coded.readFromStream(working_buffer, randblocklen);
         if (block_read != randblocklen)
         {
            errprint("read wrong amount from bucket (expected %d, got %d)", randblocklen, block_read);
         } else
            coded.removeFromStream(randblocklen);
         int block_write = coded2.writeToStream(working_buffer, randblocklen);
         if (block_write != randblocklen)
         {
            errprint("wrote wrong amount to bucket (expected %d, wrote %d)", randblocklen, block_write);
         }
         nidx += randblocklen;
         gz_dcmp->processPipeStep();
         free(working_buffer);
      }
      gz_dcmp->processPipeEnd();
      size_t new_plain_size = 0;
      char *new_plain_data = NULL;
      p_output.bucketBrigadeToBuffer(&new_plain_data, &new_plain_size);
      if (new_plain_size != randlen)
      {
         errprint("error: original data size was %" APR_SIZE_T_FMT ", new data size is %" APR_SIZE_T_FMT, (apr_size_t)randlen, new_plain_size);
         printf("old data:\n");
         StringUtils::printBinary(pseudorandom, randlen);
         printf("new data:\n");
         StringUtils::printBinary(new_plain_data, randlen);
      } else if (memcmp(pseudorandom, new_plain_data, sizeof(char)*randlen) != 0)
      {
         errprint("error: original data does not match new data");
         //printf("old data:\n");
         //StringUtils::printBinary(pseudorandom, randlen);
         //printf("new data:\n");
         //StringUtils::printBinary(new_plain_data, randlen);
      } else
      {
         printf("GZ PipeCompress(issame) test %d passed - data length %d (cmp %d)\n", i, randlen, total_length);
      }
      free(pseudorandom);
      free(new_plain_data);
   }
}
